﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Collections.ObjectModel;
using System.IO;
using Comm.Crypt;

namespace SmartMeterParamFrame.Model
{
    public class FACollection : ObservableCollection<FAinfo>
    {
        XDocument xDoc;

        /// <summary>
        /// 获取方案名称
        /// </summary>
        public string FAName
        {
            get;
            set;
        }

        /// <summary>
        /// 操作类型
        /// </summary>
        public EnumCheckType OperationType
        {
            get;
            set;
        }

        /// <summary>
        /// 生成一个空的方案实例
        /// </summary>
        public FACollection()
            : base()
        {

        }

        /// <summary>
        /// 从指定的数据中，解析方案
        /// </summary>
        /// <param name="buff"></param>
        /// <param name="crypt"></param>
        public FACollection(byte[] buff, Comm.Crypt.ICrypt crypt = null)
        {
            // add by dy
            byte[] xmlContent = buff;
            if (crypt != null)//解析数据
            {
                xmlContent = crypt.Decrypt(buff);
            }
            string xmlString = Encoding.Unicode.GetString(xmlContent);
            xDoc = XDocument.Parse(xmlString);
            FAName = xDoc.Element("FAGroup").Attribute("Name").Value;
            try
            {
                this.OperationType = (EnumCheckType)(Enum.Parse(typeof(EnumCheckType), xDoc.Element("FAGroup").Attribute("OperationType").Value));
            }
            catch
            {
                this.OperationType = EnumCheckType.复核修正复核;
            }
            foreach (XElement xe in xDoc.Descendants())
            {
                if (xe.Name != "Item") continue;
                FAinfo item = new FAinfo();

                item.PrjName = xe.Attribute("PrjName").Value;
                try
                {
                    item.PrjType = (EnumFAType)Enum.Parse(typeof(EnumFAType), xe.Attribute("PrjType").Value, true);
                }
                catch
                {
                    item.PrjType = EnumFAType.其他操作;
                }
                item.BsCode = xe.Attribute("BsCode").Value;
                item.DataDomain = xe.Attribute("DataDomain").Value;//从文件中读取出来的是底层储存格式
                try
                {
                    item.ReviewType = (EnumCheckType)Enum.Parse(typeof(EnumCheckType), xe.Attribute("ReviewType").Value, true);
                }
                catch
                {
                    item.ReviewType = EnumCheckType.复核修正复核;
                }
                item.PrjOrderID = int.Parse(xe.Attribute("PrjOrderID").Value);

                this.Add(item);
            }
        }

        /// <summary>
        /// 从指定的文件生成方案
        /// </summary>
        /// <param name="path"></param>
        /// <param name="crypt"></param>
        public FACollection(string path, Comm.Crypt.ICrypt crypt = null)
            : this(File.ReadAllBytes(path), crypt)
        {
        }


        /// <summary>
        /// 方案存储
        /// </summary>
        /// <param name="FAName"></param>
        /// <param name="path"></param>
        /// <param name="crypt"></param>
        public void Save(string FAName, string path, Comm.Crypt.ICrypt crypt = null)
        {
            if (this.Count == 0) return;
            FileStream fs = new FileStream(path, FileMode.Create);
            this.Save(fs, crypt);
            fs.Close();
        }

        /// <summary>
        /// 将数据保存到数据流中
        /// </summary>
        /// <param name="output"></param>
        /// <param name="crypt"></param>
        public void Save(Stream output, ICrypt crypt = null)
        {
            xDoc = new XDocument();
            xDoc.Add(new XElement("FAGroup", new XAttribute("Name", FAName), new XAttribute("OperationType", this.OperationType.ToString())));
            foreach (FAinfo item in this)
            {
                //如果方案是仅复核的，则所有项，都只能是，只复核
                if (this.OperationType == EnumCheckType.仅复核)
                {
                    item.ReviewType = EnumCheckType.仅复核;
                }

                XElement xe = new XElement("Item", new XAttribute("PrjName", item.PrjName),
                                                new XAttribute("PrjType", item.PrjType),
                                                new XAttribute("BsCode", item.BsCode),
                                                new XAttribute("DataDomain", item.DataDomain),//将底层数据储存到文件中
                                                new XAttribute("ReviewType", item.ReviewType),
                                                new XAttribute("PrjOrderID", item.PrjOrderID));

                xDoc.Element("FAGroup").Add(xe);
            }
            string xmlContent = xDoc.ToString();
            byte[] content = Encoding.Unicode.GetBytes(xmlContent);

            if (crypt != null)//如果数据要加密
            {
                content = crypt.Encrypt(content);
            }

            output.Write(content, 0, content.Length);
            output.Flush();
        }

        /// <summary>
        /// 将指定的类型，进行移动
        /// </summary>
        /// <param name="type">要移动的类型</param>
        /// <param name="forward">移动的值，如-1，1，2，正数表示向后移，负数向前移</param>
        /// <returns>如果发生了移动，则返回true,否则返回false,如果给定的值，超过了数据下标或者0，则移动到相应的开始或者结束</returns>
        public bool MoveGroup(FAinfo fa, int forward)
        {
            //bool ret = false;
            EnumFAType type = fa.PrjType;
            //对所有的方案进行分组
            Dictionary<EnumFAType, List<FAinfo>> groups = new Dictionary<EnumFAType, List<FAinfo>>();
            List<EnumFAType> faTypeOrder = new List<EnumFAType>();
            foreach (FAinfo fainfo in this)
            {
                //检查该项分组是否存在
                if (groups.ContainsKey(fainfo.PrjType) == false)
                {
                    groups.Add(fainfo.PrjType, new List<FAinfo>());
                }
                //检查类型是否存在
                if (faTypeOrder.Contains(fainfo.PrjType) == false)
                    faTypeOrder.Add(fainfo.PrjType);
                List<FAinfo> lstFa = groups[fainfo.PrjType];
                lstFa.Add(fainfo);
            }

            //检测是否含有与要移动的类型相同,没有则返回
            if (groups.ContainsKey(type) == false)
                return false;

            //将group中的分组，按照faTypeOrder中的顺序，进行排列
            List<FAinfo>[] fas = new List<FAinfo>[faTypeOrder.Count];
            for (int i = 0; i < faTypeOrder.Count; i++)
                fas[i] = (groups[faTypeOrder[i]]);

            //查找当前要移动类型的下标
            int index = faTypeOrder.IndexOf(type);

            //将 fas 中分组进行交换
            if (forward > 0)
            {
                //当前要移动的数据,保存起来
                List<FAinfo> current = fas[index];
                //按照forward移动次数,将前面的数据向后进行移动
                for (int j = 0; j < forward && index < fas.Length - 1; j++)
                {
                    fas[index] = fas[index + 1];
                    index++;
                }
                fas[index] = current;
            }
            else
            {
                //当前要移动的数据,保存起来
                List<FAinfo> current = fas[index];
                //按照forward移动次数,将前面的数据向后进行移动
                for (int j = 0; j < -forward && index > 0; j++)
                {
                    fas[index] = fas[index - 1];
                    index--;
                }
                fas[index] = current;
            }

            //清空当前方案集合
            this.Clear();

            //加入排序后的元素
            foreach (List<FAinfo> faInfos in fas)
            {
                foreach (FAinfo var in faInfos)
                {
                    this.Add(var);
                }
            }

            return true;
        }

        /// <summary>
        /// 移动一个参数项，
        /// 该项不会，跨其相应的参数类型
        /// </summary>
        /// <param name="fa"></param>
        /// <param name="forward">暂时只支持 -1,和1</param>
        /// <returns></returns>
        public bool MoveFainfo(FAinfo fa, int forward)
        {
            //查找到相应的下标
            int index = this.IndexOf(fa);
            int targetIndex = index + forward;

            if (targetIndex < 0 || targetIndex >= this.Count) //下标超出
                return false;

            FAinfo targetFA = this[targetIndex];
            if (targetFA.PrjType != fa.PrjType)
            {
                //要移动到的下标，与当前类型不相等
                return false;
            }

            //交换
            FAinfo tmp = this[index];
            this[index] = targetFA;
            this[targetIndex] = tmp;
            return true;
        }

        /// <summary>
        /// 增加一个元素，将自动追加到指定分组类型的后面
        /// </summary>
        /// <param name="faInfo"></param>
        public new void Add(FAinfo faInfo)
        {
            //找到与该类相同的，最后一个下标
            int i = this.Count - 1;
            bool find = false;
            for (; i >= 0; i--)
            {
                if (this[i].PrjType == faInfo.PrjType)
                {
                    find = true;
                    break;
                }
            }
            if (find == true)
            {
                this.Insert(i + 1, faInfo);
            }
            else
            {
                base.Add(faInfo);
            }
        }

        /// <summary>
        /// 返回该的字节数据流
        /// </summary>
        /// <param name="crypt"></param>
        /// <returns></returns>
        public byte[] GetBytes(ICrypt crypt = null)
        {
            MemoryStream ms = new MemoryStream();
            this.Save(ms, crypt);

            byte[] content = ms.ToArray();
            ms.Close();

            return content;
        }
    }
}
